//src/layout/components/TagsView/index.vue
<template>
  <div class="tags-view-container">
    <el-scrollbar w-full whitespace-nowrap>
      <router-link
        class="tags-view-item"
        v-for="(tag, index) in visitedViews"
        :class="{
          active: isActive(tag)
        }"
        :key="index"
        :to="{ path: tag.path, query: tag.query }"
      >
        <el-dropdown
          placement="top-start"
          trigger="contextmenu"
          @command="(command) => handleCommand(command, tag)"
        >
          <span leading-28px class="title">{{ (tag as any).title }}</span>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item command="all">关闭所有</el-dropdown-item>
              <el-dropdown-item command="other">关闭其他</el-dropdown-item>
              <el-dropdown-item command="self" v-if="!tag.meta.affix"
                >关闭</el-dropdown-item
              >
              <el-dropdown-item command="refresh">刷新</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>

        <svg-icon
          v-if="!isAffix(tag)"
          icon-name="ant-design:close-circle-outlined"
          mx-2px
          @click.prevent="closeSelectedTag(tag)"
        ></svg-icon>
      </router-link>
    </el-scrollbar>
  </div>
</template>

<script lang="ts" setup>
import { useTagsView } from "@/stores/tagsView"
import type { RouteLocationNormalizedGeneric, RouteRecordRaw } from "vue-router"
import { join } from "path-browserify"
import { routes } from "@/router/index" //从应用的路由配置文件中导入了所有的路由定义

const store = useTagsView()
const { deleteView, addView, delAllView, deleteOtherView, deleteCacheView } =
  store
//  必须采用storeToRefs 进行解构出来，否则会丧失响应式
const { visitedViews } = storeToRefs(store)

/*
useRouter 创建了一个 router 实例，该实例用于程序化的导航。
useRouter 是 Vue Router 提供的组合式 API，用于在组件中操作路由。
有了 router 实例，组件可以调用 router.push、router.replace 等方法来导航到不同的页面或视图。
*/
const router = useRouter()
/*
useRoute 创建了一个 route 实例，该实例包含了当前路由的信息。
useRoute 是 Vue Router 提供的组合式 API，用于在组件中获取当前路由的详细信息。
route 对象包含了当前路由的路径、名称、查询参数等信息，组件可以利用这些信息来判断当前活动的标签页、或者执行一些路由相关的逻辑操作。
*/
const route = useRoute()

// 判断当前路由是否激活状态
const isActive = (tag: RouteLocationNormalizedGeneric) => {
  return tag.path === route.path
}

// 判断标签是否为可关闭状态
function isAffix(tag: RouteLocationNormalizedGeneric) {
  return tag.meta.affix
}

// 添加当前路由到标签视图
const addTags = () => {
  if (route.name) {
    // 需要添加到tags中
    addView(route)
  }
}

// 导航到最后一个标签视图
const toLastView = () => {
  const lastView = visitedViews.value[visitedViews.value.length - 1]
  if (lastView) {
    router.push(lastView.path)
  } else {
    router.push("/")
  }
}

// 关闭选中的标签视图
const closeSelectedTag = (tag: RouteLocationNormalizedGeneric) => {
  // ...
  deleteView(tag)

  if (isActive(tag)) {
    // 如果删掉了自己，需要导航到当前list中的最后一个
    toLastView()
  }
}

//  此方法用于计算 哪些tag应该默认展示在列表中
function filterAffix(routes: RouteRecordRaw[], basePath = "/") {
  const tags: RouteLocationNormalizedGeneric[] = []
  for (let route of routes) {
    if (route.meta?.affix) {
      tags.push({
        name: route.name,
        path: join(basePath, route.path),
        meta: route.meta
      } as RouteLocationNormalizedGeneric)
    }
    if (route.children) {
      tags.push(...filterAffix(route.children, route.path))
    }
  }

  return tags
}

// 初始化标签视图，添加固定标签和当前路由标签
const initTags = () => {
  const filterAffixTags = filterAffix(routes)
  filterAffixTags.forEach((tag) => {
    //添加固定标签
    addView(tag)
  })
  //添加当前路由标签
  addTags()
}

// 页面加载后 需要初始化固定 + 默认当前路径的
onMounted(() => {
  initTags()
})

//  路径变化时重新添加
watch(() => route.path, addTags)

// 点击菜单

const enum CommandType {
  All = "all",
  Other = "other",
  Self = "self",
  Refresh = "refresh"
}

// 处理菜单命令
const handleCommand = (
  command: CommandType,
  view: RouteLocationNormalizedGeneric
) => {
  switch (command) {
    case CommandType.All:
      delAllView()
      break
    case CommandType.Other:
      deleteOtherView(view)
      if (!isActive(view)) {
        router.push(view.path)
      }
      break
    case CommandType.Self:
      closeSelectedTag(view)
      break
    case CommandType.Refresh:
      // 如果本次路径和上次路径相同，刷新会没有效果
      // 解决方法：跳转到专门做刷新的一个路由，在通过这个路由回来即可
      deleteCacheView(view)

      router.push("/redirect" + view.path)
      break
  }
}
</script>

<style scoped>
.tags-view-container {
  @apply w-full overflow-hidden  @apply h-[var(--tagsview-height)] shadow-sm shadow-gray-300 bg-gray-100;
}
.tags-view-item {
  @apply inline-block h-28px leading-28px  px-12px mx-3px  text-black mt-1 bg-gray-200;
  &.active {
    @apply text-white border-none bg-blue;
    .title {
      @apply text-white;
    }
    &::before {
      content: "";
      @apply inline-block w-8px h-8px rounded-full  bg-white mr-3px;
    }
  }
}
</style>
